/**
 * 工具包头文件
 * 张勇 41204@qq.com
 */

#pragma once

#ifdef _WIN32
    // define something for Windows (32-bit and 64-bit, this part is common)
    #ifdef _WIN64
        // define something for Windows (64-bit only)
        //#pragma message("OS = Windows 64-bit")
    #else
        // define something for Windows (32-bit only)
        //#pragma message("OS = Windows 32-bit")
    #endif
#elif __APPLE__
    #if TARGET_IPHONE_SIMULATOR
        //#pragma message("OS = iOS Simulator")
    #elif TARGET_OS_IPHONE
        //#pragma message("OS = iOS device")
    #elif TARGET_OS_MAC
        // Other kinds of Mac OS
        //#pragma message("OS = Mac OS")
    #else
        #error "Unknown Apple platform !"
    #endif
#elif __ANDROID__
    //#pragma message("OS = Android")
#elif __linux__
    //#pragma message("OS = Linux")
#elif __unix__
    // all unices not caught above
    //#pragma message("OS = Unix")
#elif defined(_POSIX_VERSION)
    //#pragma message("OS = POSIX")
#else
    //#error "Unknown OS !"
#endif

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#if (__linux__ || __unix__)
#include <unistd.h>
#endif

#include "cccompat.h"
#include "std_err.h"
#include "isoc_patch.h"
#include "posix_patch.h"
#include "unios.h"

#define MAXU32 	((u32)0xFFFFFFFF)
#define MAXU16	((u16)0xFFFF)

/// u16 字节序颠倒
#define BL16(w)  		((((w)&0x00FF)<<8) | (((w)&0xFF00)>>8))
/// u32 字节序颠倒
#define BL32(dw) 		((((dw)&0x000000FF)<<24) | (((dw)&0x0000FF00)<<8) | (((dw)&0x00FF0000)>>8) | (((dw)&0xFF000000)>>24))
/// 第 n 位的掩码
#define BV(n) 			(1U<<(n))
/// 取 v 的第 n 位的值
#define BITx(v, n)		(((v) >> (n)) & 1)
/// 取一个字节数组的第 n 位的值
#define BITn(buff, n)	BITx((buff)[(n)>>3], (n)&7)
/// 设置 v 的第 n 位为 1
#define BitSet(v, n)	((v) |= BV(n))
/// 设置 v 的第 n 位为 0
#define BitReset(v, n)	((v) &= (~BV(n)))
/// 把 v 的第 n 位取反
#define BitInvert(v, n)	((v) ^= BV(n))
/// 构造最低位为 n 个 1 的整数
#define NBITS1(n)       ((1U << (n)) - 1)

/// 取字节的高 4 位
#define HI4B(b)   (((b)&0xF0) >>4)
/// 取字节的低 4 位
#define LO4B(b)   ((b)&0x0F)

/// 取 u16 的高字节
#ifndef HIBYTE
#define HIBYTE(w)   (((w)&0xFF00) >>8)
#endif
/// 取 u16 的低字节
#ifndef LOBYTE
#define LOBYTE(w)   ((w)&0x00FF)
#endif

/// 取 u32 的高 u16
#ifndef HIWORD
#define HIWORD(dw)  (((dw)&0xFFFF0000)>>16)
#endif
/// 取 u32 的低 u16
#ifndef LOWORD
#define LOWORD(dw)  ((dw)&0x0000FFFF)
#endif

/// 取 u32 的第 1 字节(大端在前)
#define BHH(dw) HIBYTE(HIWORD(dw))
/// 取 u32 的第 2 字节(大端在前)
#define BHL(dw) LOBYTE(HIWORD(dw))
/// 取 u32 的第 3 字节(大端在前)
#define BLH(dw) HIBYTE(LOWORD(dw))
/// 取 u32 的第 4 字节(大端在前)
#define BLL(dw) LOBYTE(LOWORD(dw))

#define MAKE_BYTE(H4B, L4B)				((uint8_t)((((H4B)<<4)&0xF0) | ((L4B)&0x0F)))
#define MAKE_WORD(BH, BL)               ((uint16_t)((((BH)&0xFF)<<8) | ((BL)&0xFF)))
#define MAKE_DWORD(BHH, BHL, BLH, BLL)  ((uint32_t)((MAKE_WORD((BHH), (BHL))<<16) | MAKE_WORD((BLH), (BLL))))

/// 生成一个表示日期的16位整数：年:7-月:4-日:5，表示 2000/1/1 ~ 2127/12/31 之间的所有日期
#define MAKE_DATE16(y, m, d)            ((uint16_t)((((y)%1000)<<9) | ((m)<<5) | (d)))

/// 生成版本号字符串
#define MAKE_VERSION_STRING(major, minor, build)  (QUOTE(major) "." QUOTE(minor) "." QUOTE(build))

/// bool 型转换为 true/false 字符串
#define strbool(b) ((b) ? "true" : "false")

/// 绝对值
#define ABS(num) (((num)>=0)?(num):-(num))
/// 取正负符号(1表示正数，-1表示负数)
#define SIGN(num) (((num)>=0)?(1):(-1))
/// 求差值(绝对值)
#define DIFF(a, b) (((a)>(b))?((a)-(b)):((b)-(a)))
/// 判断两个浮点数是否近似相等
#define FEQ(f1, f2) ((f1 >= f2) ? ((f1 - f2) <= 0.000001) : ((f2 - f1) <= 0.000001))
/// 整数4舍5入除法
#define IDIV45(a, b) (((a)+((b)>>1))/(b))
/// 浮点数4舍5入除法
#define FDIV45(a, b) ((a)*1.0/(b)+0.5)
/// 浮点数四舍五入取整
#define F2I(f) ((int)((f) + 0.5f))
#define F2L(f) ((long)((f) + 0.5f))
#define F2UI(f) ((unsigned int)((f) + 0.5f))
#define F2UL(f) ((unsigned long)((f) + 0.5f))

/// num1, num2 之前的误差是否小于 err
#define ISABOUT(num1, num2, err) 	((((num1)-(num2))>(err) || ((num1)-(num2))<(-(err)))?false:true)
/// num 是否在 [min, max] 之间
#define ISBETWEEN(num, min, max) 	(((num)>=(min) && ((num)<=(max)))?true:false)
/// num1 和 num2 相差多少(%)
#define ISTIMES(num1, num2) 		((num1>num2)?(num1*100L/num2):(num2*100L/num1))
/// num1 和 num2 误差是多少(%)
#define PERR(num1, num2)			(ISTIMES(num1, num2) - 100)

#ifndef min
#define min(a, b) ((a)<(b)?(a):(b))
#endif

#ifndef max
#define max(a, b) ((a)>(b)?(a):(b))
#endif

/// 取3个数的最大值
#define min3(a, b, c) min(min((a), (b)), (c))
#define max3(a, b, c) max(max((a), (b)), (c))

/// 计算一个数组中元素的数量
#define COUNT(v) (sizeof(v)/sizeof(v[0]))

/// 是否是有效的 HEX 字符
#define IS_HEX_CHAR(ch) ( ((ch>='0') && (ch<='9')) || ((ch>='A') && (ch<='F')) || ((ch>='a') && (ch<='f')) )

/// 无操作
#define NULLOP()    do {} while(0)
/// 无操作返回
#ifdef __cplusplus
    #define NOPRET(x)   (x)
#else
    #ifdef _MSC_VER
        #define NOPRET(x)   (x)
    #else
        #define NOPRET(x)   (x)
    #endif
#endif

/// 避免未引用警告
static INLINE void _unref(const void *dummy) {}
#define UNREF(x)   _unref((const void *)(&(x)))
#ifndef UNUSED
#define UNUSED(x)  (void)x
#endif

/// 多行语句宏定义
#define st(x)      do { x } while (__LINE__ == -1)

/// 引用
#define QUOTE(x) #x
/// 拼接版本号字符串
#define MAKE_VERSION_STRING(major, minor, build) (QUOTE(major) "." QUOTE(minor) "." QUOTE(build))

// 版本号处理
typedef union {
	uint32_t dw;
	struct {
		uint8_t majorVersion;  ///< 主版本号
		uint8_t minorVersion;  ///< 小版本号
		uint16_t buildNumber;  ///< Build Number
	} st;
} version_t;

/// @brief 与另一个版本相比较
/// @retval >0: 本版本号大于指定版本号
/// @retval =0: 本版本号等于指定版本号
/// @retval <0: 本版本号小于指定版本号
int version_compare(version_t* ver1, version_t* ver2);

#ifdef __cplusplus
extern "C" {
#endif

/**
 * 设置调试标志位
 * @param 第几个标志位。应小于 size_t 的位数。
 * @param 设置为 true 或 false
 */
void dbgflag_set(unsigned int idx, bool val);

/**
 * 获取调试标志位
 * @param 第几个标志位。应小于 size_t 的位数。
 */
bool dbgflag_get(unsigned int idx);

/**
 * 触发调试断点
 */
void dbgbreak();

// 字节对齐
size_t align(size_t addr, size_t nbytes);

/**
 * 把指定缓冲区数据拷贝到一个需要 32 位对齐的内存区域中
 * @param dst32 目标缓冲区。需要按 4 字节寻址，但起始地址、结束地址可以不按 4 字节对齐。
 * @param src 源缓冲区。假定无字节对齐要求（可以按 1 字节寻址）。
 * @param size 要拷贝的数据长度，可以是任意长度字节。
 * @return 拷贝的字节数
 */
size_t memcpy_to_aligned32(void *dst32, const void *src, size_t size);
size_t memcpy_to_aligned64(void *dst64, const void *src, size_t size);
static INLINE size_t memcpy_to_aligned8(void *dst8, const void *src, size_t size) { return memcpy(dst8, src, size) ? size : 0; }

/**
 * 把一个需要 32 位对齐的内存区域中的数据拷贝到指定缓冲区
 * @param dst 目标缓冲区。假定无字节对齐要求（可以按 1 字节寻址）。
 * @param src32 源缓冲区。需要按 4 字节寻址，但起始地址、结束地址可以不按 4 字节对齐。
 * @param size 要拷贝的数据长度，可以是任意长度字节。
 * @return 拷贝的字节数
 */
size_t memcpy_from_aligned32(void *dst, const void *src32, size_t size);
size_t memcpy_from_aligned64(void *dst, const void *src64, size_t size);
static INLINE size_t memcpy_from_aligned8(void *dst, const void *src8, size_t size) { return memcpy(dst, src8, size) ? size : 0; }

/**
 * 从一个只能以 32 位对齐的方式访问的内存中，读取 32 位整数
 * @param addr 地址
 * @param offset 偏移
 * @return 读取的整数
 */
static INLINE uint32_t read32_from_aligned32(void *addr, size_t offset) {
    uint32_t val = 0;
    memcpy_from_aligned32(&val, (void*)(((size_t)addr) + offset), sizeof(uint32_t));
    return val;
}

/**
 * 从一个只能以 32 位对齐的方式访问的内存中，读取 64 位整数
 * @param addr 地址
 * @param offset 偏移
 * @return 读取的整数
 */
static INLINE uint64_t read64_from_aligned32(void *addr, size_t offset) {
    uint64_t val = 0;
    memcpy_from_aligned32(&val, (void*)(((size_t)addr) + offset), sizeof(uint64_t));
    return val;
}

/**
 * 向一个只能以 32 位对齐的方式访问的内存写入 32 位整数
 * @param addr 地址
 * @param offset 偏移
 * @param val 要写入的整数
 */
static INLINE void write32_to_aligned32(void *addr, size_t offset, uint32_t val) {
    memcpy_to_aligned32((void*)(((size_t)addr) + offset), &val, sizeof(uint32_t));
}

/**
 * 向一个只能以 32 位对齐的方式访问的内存写入 64 位整数
 * @param addr 地址
 * @param offset 偏移
 * @param val 要写入的整数
 */
static INLINE void write64_to_aligned32(void *addr, size_t offset, uint64_t val) {
    memcpy_to_aligned32((void*)(((size_t)addr) + offset), &val, sizeof(uint64_t));
}

/**
 * 计算一个数字有多少个 bit 是 1。比如 0x0B 和 0x07 都有 3 个 bit 是 1。
 */
size_t count_bit1(size_t num);

/**
 * 计算一个数字有多少个有效的 4bit。比如 0x0001234567 有 7 个有效的 4bit。
 */
size_t count_hex(size_t num);

/**
 * 把数字转换成 HEX 字符串
 * @param num 要转换的数字
 * @param width 要转换的字符串长度（最大 16 个 HEX 字符）。数字大小不足时，前面补 '0'；大于指定长度时，高位截断。
 * @return 转换后的 HEX 字符串。
 * @note 注意：转换后的 HEX 字符串是一个局部 static 变量，仅在下次调用 tohex() 前有效 ！！！
 */
const char* to_hex(size_t num, size_t width);
/**
 * @brief byte 数组转换成十六进制字符串
 * @param bytes 要转换的字节数组
 * @param start 起始位置
 * @param len 要转换的长度
 * @param gap 间隔字符串；注意：最后个字节后面不会加间隔字符串！
 * @param hexBuf 用于存储十六进制字符串的缓冲区，大小为 hexBufLen
 * @param hexBufLen 十六进制字符串缓冲区大小(注意：长度要包含一个字符串结束符'\0')
 * @return 正常情况下返回 hexBuf；如果缓冲区大小不足，则返回 NULL
 */
char* bytes_to_hexstr(const uint8_t* bytes, int start, int len, char* gap, char *hexBuf, int hexBufLen);

/**
 * @brief 十六进制字符串转换成 byte 数组
 * @param hexBuf 十六进制字符串
 * @param bytes 用于存储转换后的字节缓冲区
 * @param len 字节缓冲区的长度
 * @return 正常情况下返回转换后的有效字节长度；如果缓冲区大小不足，则返回 -1
 */
int hexstr_to_bytes(const char *hexBuf, uint8_t* bytes, int len);

/**
 * @brief MAC 地址数组转换成十六进制字符串
 * @attention 注意：此函数非线程安全！下一次调用会覆盖上一次的调用！
 * @param mac MAC 地址数组
 * @return HexString
 */
char* mac_to_hexstr(const void *mac, char gap);

/// @brief MAC 地址是否相同
bool mac_match(const void *mac1, const void *mac2);

/// @brief 字节流反序
uint8_t* reverse_bytes(uint8_t *bytes, int len);

/// @brief 从后向前进行内存拷贝(主要用于内存向后搬移时，不产生覆盖)
void* reverse_memcpy(void *dst, const void *src, size_t len);

/// 把用 HEX 表示的十进制数字，转换成实际的整数
/// 如："\x20" => 20, "\x19" => 19
uint8_t hexdec2int(uint8_t hexdec);
/// 把整数转换成用 HEX 表示的十进制数字
/// 如：20 => "\x20", 19 => "\x19"
uint8_t int2hexdec(uint8_t ch);

/// 生成一个 n 以内的随机数
uint32_t nrand(uint32_t n);
/// 生成一个介于 [n1, n2] 之间的随机数
uint32_t xrand(uint32_t n1, uint32_t n2);

/**
 * 等同于标准 C 的 putchar() 函数声明
 */
typedef int (*user_putchar_t)(int ch);

/**
 * 打印输出二进制数据
 * @param buf 二进制数据缓冲区
 * @param len 二进制数据长度
 * @param chars_per_line 每行输出多少个字节
 * @param offset >= 0 表示要显示的数据偏移地址。< 0 时不显示。
 * @param show_ascii 是否显示可表示为 ASCII 字符的数据对应的字符。
 * @param user_putchar 用户指定用于字符输出的的函数。如果为 NULL，则使用标准 C 的 putchar() 函数。
 */
void dumpbin(const void *buf, size_t len, size_t chars_per_line, long offset, bool show_ascii, user_putchar_t user_putchar);

// 计算缓冲区中数据的字节累加和
uint32_t sum_bytes(const void* data, size_t len);


#ifdef _WIN32
// 获取最后一次 Windows API 错误信息
const char* get_windows_error_msg();

/**
* 把宽字符串转换为 ANSI 字符串（使用指定的 code page）
* @param wstr 宽字符串
* @param cp 使用的 code page。如 CP_ACP 表示使用系统默认的 Windows ANSI 代码页。
*   CP_ACP                    0           // default to ANSI code page
*   CP_OEMCP                  1           // default to OEM  code page
*   CP_MACCP                  2           // default to MAC  code page
*   CP_THREAD_ACP             3           // current thread's ANSI code page
*   CP_SYMBOL                 42          // SYMBOL translations
*   CP_UTF7                   65000       // UTF-7 translation
*   CP_UTF8                   65001       // UTF-8 translation
* @return 成功则返回 ANSI 字符串；否则返回 NULL
*/
char* wchar_to_char(const wchar_t* wstr, unsigned int cp);

void* find_main_window(unsigned long process_id);

void* find_current_process_main_window();

void* find_sibling_window(void* parent, const char* clazz, const char* title);

size_t console_read_key(size_t wait_timeout_ms);
#endif


#ifdef __cplusplus
} //extern "C"
#endif
